{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1Pi_B2cvdBiW"
      },
      "source": [
        "##### Copyright 2019 The TF-Agents Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "nQnmcm0oI1Q-"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "GiI8CZYWcJ5n"
      },
      "source": [
        "# ネットワーク\n",
        "\n",
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/agents/tutorials/8_networks_tutorial\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\"> TensorFlow.orgで表示</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/agents/tutorials/8_networks_tutorial.ipynb\">     <img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Google Colabで実行</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ja/agents/tutorials/8_networks_tutorial.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub でソースを表示{</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/agents/tutorials/8_networks_tutorial.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">ノートブックをダウンロード/a0}</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "31uij8nIo5bG"
      },
      "source": [
        "## はじめに\n",
        "\n",
        "このコラボでは、エージェントにカスタムネットワークを定義する方法について説明します。ネットワークはエージェントによってトレーニングされるモデルを定義するのに役立ちます。TF-Agents では、エージェント間で役に立つ複数種類のネットワークがあります。\n",
        "\n",
        "**メインネットワーク**\n",
        "\n",
        "- **QNetwork**: このネットワークは別々のアクションを持つ環境の Qlearning で使用され、観測をマッピングしてそれぞれの可能なアクションの推定値を評価します。\n",
        "- **CriticNetworks**: 文献では `ValueNetworks` とも呼ばれており、いくつかの状態をあるポリシーの期待リターンの推定値にマッピングする Value 関数のいくつかのバージョンを推定することを学習します。これらのネットワークは、エージェントが現在どの程度良好な状態にあるのかを推定します。\n",
        "- **ActorNetworks**: 観測からアクションへのマッピングを学習します。このネットワークは通常、アクションを生成するためにポリシーによって使用されます。\n",
        "- **ActorDistributionNetworks**: `ActorNetworks` に似ていますが、ポリシーがサンプリングしてアクションを生成できる分布を生成します。\n",
        "\n",
        "**ヘルパーネットワーク**\n",
        "\n",
        "- **EncodingNetwork**: ユーザーがネットワークの入力に適用する前処理レイヤーのマッピングを簡単に定義できるようにします。\n",
        "- **DynamicUnrollLayer**: 時系列に適用されると、エピソード境界でネットワークの状態を自動的にリセットします。\n",
        "- **ProjectionNetwork**: `CategoricalProjectionNetwork` や `NormalProjectionNetwork` のようなネットワークは入力を受け付け、カテゴリカル分布や正規分布の生成に必要なパブリックベータを生成します。\n",
        "\n",
        "TF-Agents のすべての例にはネットワークが事前に構成されています。ただし、それらのネットワークは複雑な観測を処理するようにはセットアップされていません。\n",
        "\n",
        "複数の観測/アクションを公開する環境があり、ネットワークのカスタマイズが必要な方にはこのチュートリアルが適しています。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Wmk1GBT9cPqC"
      },
      "source": [
        "## セットアップ"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "uhGeL1Kpc3Pw"
      },
      "source": [
        "tf-agents をまだインストールしていない場合は、次を実行します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "xsLTHlVdiZP3"
      },
      "outputs": [],
      "source": [
        "!pip install tf-agents"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "sdvop99JlYSM"
      },
      "outputs": [],
      "source": [
        "from __future__ import absolute_import\n",
        "from __future__ import division\n",
        "from __future__ import print_function\n",
        "\n",
        "import abc\n",
        "import tensorflow as tf\n",
        "import numpy as np\n",
        "\n",
        "from tf_agents.environments import random_py_environment\n",
        "from tf_agents.environments import tf_py_environment\n",
        "from tf_agents.networks import encoding_network\n",
        "from tf_agents.networks import network\n",
        "from tf_agents.networks import utils\n",
        "from tf_agents.specs import array_spec\n",
        "from tf_agents.utils import common as common_utils\n",
        "from tf_agents.utils import nest_utils\n",
        "\n",
        "tf.compat.v1.enable_v2_behavior()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ums84-YP_21F"
      },
      "source": [
        "## ネットワークの定義\n",
        "\n",
        "### ネットワーク API\n",
        "\n",
        "TF-Agents では Keras [ネットワーク](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/engine/network.py)からサブクラスを作っています。これを使用して以下を実行できます。\n",
        "\n",
        "- ターゲットネットワークの作成時に必要なコピー操作を単純化する。\n",
        "- `network.variables()` の呼び出し時に自動変数の作成を実行する。\n",
        "- ネットワークの input_specs を基に入力を検証する。\n",
        "\n",
        "##EncodingNetwork 上述のように `EncodingNetwork` を使用すると、ネットワークの入力に適用する前処理レイヤーのマッピングを簡単に定義していくつかのエンコードを生成することができます。\n",
        "\n",
        "EncodingNetwork は、次のほとんどのオプションレイヤーから構成されています。\n",
        "\n",
        "- 前処理レイヤー\n",
        "- 前処理コンバイナー\n",
        "- Conv2D\n",
        "- Flatten\n",
        "- Dense\n",
        "\n",
        "エンコーディングネットワークの特徴は、入力の前処理が適用されることです。入力の前処理は、`preprocessing_layers` および `preprocessing_combiner` レイヤーを介して実行可能です。これらはそれぞれネスト構造として指定できます。`preprocessing_layers` ネストが `input_tensor_spec` よりも浅い場合、これらのレイヤーはサブネストを取得します。例を以下に示します。\n",
        "\n",
        "```\n",
        "input_tensor_spec = ([TensorSpec(3)] * 2, [TensorSpec(3)] * 5) preprocessing_layers = (Layer1(), Layer2())\n",
        "```\n",
        "\n",
        "この場合、前処理によって以下が呼び出されます。\n",
        "\n",
        "```\n",
        "preprocessed = [preprocessing_layers[0](observations[0]),                 preprocessing_layers[1](obsrevations[1])]\n",
        "```\n",
        "\n",
        "しかし、次の場合はどうなるでしょうか。\n",
        "\n",
        "```\n",
        "preprocessing_layers = ([Layer1() for _ in range(2)],                         [Layer2() for _ in range(5)])\n",
        "```\n",
        "\n",
        "この場合、前処理によって以下が呼び出されます。\n",
        "\n",
        "```python\n",
        "preprocessed = [   layer(obs) for layer, obs in zip(flatten(preprocessing_layers),                                     flatten(observations)) ]\n",
        "```\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RP3H1bw0ykro"
      },
      "source": [
        "### カスタムネットワーク\n",
        "\n",
        "独自ネットワークを作成するには、`__init__` メソッドと `call` メソッドをオーバーライドするだけです。`EncodingNetworks` について学習した内容を使用してカスタムネットワークを作成し、画像とベクトルを含む観測を取得する ActorNetwork を作成してみましょう。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Zp0TjAJhYo4s"
      },
      "outputs": [],
      "source": [
        "class ActorNetwork(network.Network):\n",
        "\n",
        "  def __init__(self,\n",
        "               observation_spec,\n",
        "               action_spec,\n",
        "               preprocessing_layers=None,\n",
        "               preprocessing_combiner=None,\n",
        "               conv_layer_params=None,\n",
        "               fc_layer_params=(75, 40),\n",
        "               dropout_layer_params=None,\n",
        "               activation_fn=tf.keras.activations.relu,\n",
        "               enable_last_layer_zero_initializer=False,\n",
        "               name='ActorNetwork'):\n",
        "    super(ActorNetwork, self).__init__(\n",
        "        input_tensor_spec=observation_spec, state_spec=(), name=name)\n",
        "\n",
        "    # For simplicity we will only support a single action float output.\n",
        "    self._action_spec = action_spec\n",
        "    flat_action_spec = tf.nest.flatten(action_spec)\n",
        "    if len(flat_action_spec) > 1:\n",
        "      raise ValueError('Only a single action is supported by this network')\n",
        "    self._single_action_spec = flat_action_spec[0]\n",
        "    if self._single_action_spec.dtype not in [tf.float32, tf.float64]:\n",
        "      raise ValueError('Only float actions are supported by this network.')\n",
        "\n",
        "    kernel_initializer = tf.keras.initializers.VarianceScaling(\n",
        "        scale=1. / 3., mode='fan_in', distribution='uniform')\n",
        "    self._encoder = encoding_network.EncodingNetwork(\n",
        "        observation_spec,\n",
        "        preprocessing_layers=preprocessing_layers,\n",
        "        preprocessing_combiner=preprocessing_combiner,\n",
        "        conv_layer_params=conv_layer_params,\n",
        "        fc_layer_params=fc_layer_params,\n",
        "        dropout_layer_params=dropout_layer_params,\n",
        "        activation_fn=activation_fn,\n",
        "        kernel_initializer=kernel_initializer,\n",
        "        batch_squash=False)\n",
        "\n",
        "    initializer = tf.keras.initializers.RandomUniform(\n",
        "        minval=-0.003, maxval=0.003)\n",
        "\n",
        "    self._action_projection_layer = tf.keras.layers.Dense(\n",
        "        flat_action_spec[0].shape.num_elements(),\n",
        "        activation=tf.keras.activations.tanh,\n",
        "        kernel_initializer=initializer,\n",
        "        name='action')\n",
        "\n",
        "  def call(self, observations, step_type=(), network_state=()):\n",
        "    outer_rank = nest_utils.get_outer_rank(observations, self.input_tensor_spec)\n",
        "    # We use batch_squash here in case the observations have a time sequence\n",
        "    # compoment.\n",
        "    batch_squash = utils.BatchSquash(outer_rank)\n",
        "    observations = tf.nest.map_structure(batch_squash.flatten, observations)\n",
        "\n",
        "    state, network_state = self._encoder(\n",
        "        observations, step_type=step_type, network_state=network_state)\n",
        "    actions = self._action_projection_layer(state)\n",
        "    actions = common_utils.scale_to_spec(actions, self._single_action_spec)\n",
        "    actions = batch_squash.unflatten(actions)\n",
        "    return tf.nest.pack_sequence_as(self._action_spec, [actions]), network_state"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Fm-MbMMLYiZj"
      },
      "source": [
        "`RandomPyEnvironment` を作成し、構造化した観測を生成して実装を検証しましょう。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "E2XoNuuD66s5"
      },
      "outputs": [],
      "source": [
        "action_spec = array_spec.BoundedArraySpec((3,), np.float32, minimum=0, maximum=10)\n",
        "observation_spec =  {\n",
        "    'image': array_spec.BoundedArraySpec((16, 16, 3), np.float32, minimum=0,\n",
        "                                        maximum=255),\n",
        "    'vector': array_spec.BoundedArraySpec((5,), np.float32, minimum=-100,\n",
        "                                          maximum=100)}\n",
        "\n",
        "random_env = random_py_environment.RandomPyEnvironment(observation_spec, action_spec=action_spec)\n",
        "\n",
        "# Convert the environment to a TFEnv to generate tensors.\n",
        "tf_env = tf_py_environment.TFPyEnvironment(random_env)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LM3uDTD7TNVx"
      },
      "source": [
        "観測をディクショナリとして定義しましたので、観測を処理する前処理レイヤーを作成する必要があります。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "r9U6JVevTAJw"
      },
      "outputs": [],
      "source": [
        "preprocessing_layers = {\n",
        "    'image': tf.keras.models.Sequential([tf.keras.layers.Conv2D(8, 4),\n",
        "                                        tf.keras.layers.Flatten()]),\n",
        "    'vector': tf.keras.layers.Dense(5)\n",
        "    }\n",
        "preprocessing_combiner = tf.keras.layers.Concatenate(axis=-1)\n",
        "actor = ActorNetwork(tf_env.observation_spec(), \n",
        "                     tf_env.action_spec(),\n",
        "                     preprocessing_layers=preprocessing_layers,\n",
        "                     preprocessing_combiner=preprocessing_combiner)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "mM9qedlwc41U"
      },
      "source": [
        "これでアクターネットワークを用意できたので、環境から観測を処理できるようになりました。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "JOkkeu7vXoei"
      },
      "outputs": [],
      "source": [
        "time_step = tf_env.reset()\n",
        "actor(time_step.observation, time_step.step_type)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ALGxaQLWc9GI"
      },
      "source": [
        "これと同じ戦略をエージェントが使用するメインネットワークのカスタマイズに使用できます。任意の前処理を定義し、それをネットワークの残りの部分に接続できます。独自のものを定義する場合は、ネットワークの出力レイヤーの定義が一致していることを確認してください。"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "8_networks_tutorial.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
